package com.tsfyun.scm.security.config;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.tsfyun.common.base.constant.LoginConstant;
import com.tsfyun.common.base.enums.core.DeviceEnum;
import com.tsfyun.common.base.security.LoginVO;
import com.tsfyun.common.base.security.SecurityUtil;
import com.tsfyun.common.base.util.DeviceUtil;
import com.tsfyun.common.base.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * @Description: token续签拦截器
 * @since Created in 2020/02/23 17:45
 */
@Component
@Slf4j
public class TokenRenewalInterceptor extends HandlerInterceptorAdapter {

    @Value("${auth.expireSecond:18000}")
    private long tokenExpireTime;

    @Autowired
    @Qualifier("loginRedisScript")
    private DefaultRedisScript<Long> script;

    @Autowired
    @Qualifier("stringRedisTemplate")
    private RedisTemplate stringRedisTemplate;

    private AntPathMatcher pathMatcher = new AntPathMatcher();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        boolean flag = autoRenewal(request);
        //此处可以考虑增加拦截校验，客户端/销售端不允许访问管理端的接口，可以通过@PreAuthorize("hasRole('client')")配置
        return flag;
    }


    /**
     * token续签
     * @param request
     * @return
     */
    public boolean autoRenewal(HttpServletRequest request) {
        //websocket请求不自动续租，因为是由于程序导致的请求，而非用户主动触发的请求；增加任务数量请求（前端会非客户主动触发，不参与续签）
        if (pathMatcher.match("/websocket", request.getRequestURI()) || pathMatcher.match("/sock-js", request.getRequestURI())
            || pathMatcher.match("/taskNotice/count",request.getRequestURI())) {
            return true;
        }
        try {
            LoginVO loginVO = SecurityUtil.getCurrent();
            Optional.ofNullable(loginVO).ifPresent(r->{
                String loginToken = SecurityUtil.getToken();
                Object tokenVOObj = stringRedisTemplate.opsForValue().get(LoginConstant.TOKEN + loginToken);
                long doTokenExpireTime = tokenExpireTime;
                //改成从请求头中获取平台类型，只有PC端续签，其他客户端则定期3天凌晨失效（防止正在操作退出）
                DeviceEnum deviceEnum = DeviceUtil.getPlatform();
                if(Objects.equals(deviceEnum,DeviceEnum.PC)) {
                    //token自动续费
                    LocalDateTime t1 = LocalDateTime.now().plusSeconds(doTokenExpireTime);
                    ZoneId zone = ZoneId.systemDefault();
                    Instant instant = t1.atZone(zone).toInstant();
                    Date date = Date.from(instant);
                    int seconds = date != null ? Long.valueOf((date.getTime() - System.currentTimeMillis()) / 1000L).intValue() : 0;
                    //续签时间续差值
                    List<String> keys = Lists.newArrayList(LoginConstant.TOKEN  + loginToken, LoginConstant.AUTH  + loginVO.getPersonId().toString());
                    Long freshTokenResult = (Long) stringRedisTemplate.execute(this.script, new StringRedisSerializer(), new StringRedisSerializer(), keys, StringUtils.null2EmptyWithTrim(tokenVOObj), JSONObject.toJSONString(loginVO), seconds + "");
                    if (1 != freshTokenResult) {
                        log.error("token:{}续费失败", loginToken);
                    }
                }
            });
        } catch (Exception e) {
            log.error("token续签失败",e);
        }
        return true;
    }

}
